package com.bluexmicro.bluetooth

import android.bluetooth.le.ScanResult
import android.content.Context
import com.bluexmicro.bluetooth.model.ScanMode
import com.bluexmicro.bluetooth.peripheral.BluetoothValue
import com.bluexmicro.bluetooth.peripheral.PeripheralManager
import com.bluexmicro.bluetooth.peripheral.PeripheralState
import com.bluexmicro.bluetooth.scan.ScannerImpl
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import java.lang.ref.WeakReference

object BluetoothCentral {

    private var contextWeakRef = WeakReference<Context>(null)

    private val peripheralManager = PeripheralManager()

    private fun requestContext(): Context {
        return requireNotNull(contextWeakRef.get()) { "Please call BluetoothCentral.setup() first" }
    }

    fun setup(context: Context) {
        contextWeakRef = WeakReference(context.applicationContext)
        peripheralManager.launch(context)
    }

    ///////////////////////////////////////////////////////////////////////////
    // Scanner
    ///////////////////////////////////////////////////////////////////////////
    private val scanner = ScannerImpl()

    val isScanning: StateFlow<Boolean> = scanner.isScanning

    val bluetoothError = scanner.bluetoothError

    fun scanForPeripherals(scanMode: ScanMode = ScanMode.BALANCED) =
        scanner.scanForPeripherals(requestContext(), scanMode)

    fun stopScan() = scanner.stopScan()

    fun delayStopScan() = scanner.delayStop()


    /**
     * @param expiredTimeSeconds 取最近10s内扫描到的设备(expiredTimeSeconds = 10s),等于0时为扫描到的所有设备
     */
    fun fetchScanResults(expiredTimeSeconds: Long = 0L): List<ScanResult> =
        scanner.fetchScanResults(expiredTimeSeconds)

    fun clearCache() = scanner.clearCache()

    ///////////////////////////////////////////////////////////////////////////
    // Peripheral
    ///////////////////////////////////////////////////////////////////////////

    fun retrieveConnectedPeripherals(): List<PeripheralManager.BluetoothPeripheral> =
        peripheralManager.retrieveConnectedPeripherals()

    fun retrievePeripherals(context: Context, mac: String): PeripheralManager.BluetoothPeripheral? =
        peripheralManager.retrievePeripherals(context, mac)

    fun observeCharacteristicValue(
        mac: String,
        service: String,
        characteristic: String
    ): Flow<BluetoothValue> =
        peripheralManager.observeCharacteristicValue(mac, service, characteristic)

    fun observePeripheralState(mac: String): Flow<PeripheralState> =
        peripheralManager.observePeripheralState(mac)

    suspend fun connect(
        context: Context,
        peripheral: PeripheralManager.BluetoothPeripheral,
        autoConnect: Boolean = false
    ) = peripheralManager.connect(context, peripheral, autoConnect)

    fun release() = peripheralManager.release(requestContext())

    fun releasePeripheral(
        peripheral: PeripheralManager.BluetoothPeripheral,
        disconnect: Boolean = true
    ) = peripheralManager.releaseBluetoothPeripheral(peripheral, disconnect)

    fun releasePeripheral(context: Context, mac: String, disconnect: Boolean = true) {
        retrievePeripherals(context, mac)?.apply {
            peripheralManager.releaseBluetoothPeripheral(this, disconnect)
        }
    }

}